<?php

/**
 * make sure this is only included once!
 */
if ( !defined("TEMPLATE_INCLUDED") ) {
	define("TEMPLATE_INCLUDED",1);
	
/**
 * 模版引擎父类。
 *
 * 模版引擎父类，用来声明所有子类所需要的固定变量。
 * @package     template  (所属的模块名称)
 * @author      Jerry <jerryjiang15@163.com>
 * @version     $ID$
 */
	class Template 
	{
		/**
		 * reference to a {@link Logger} object
         * @see Logger
		 * @var object Logger
		 */
		var $logger;
		/**
		 * 存放模版目录
		 * @var string
		 */
		var $template_dir = 'templates';
		/**
		 * 存放模版临时编译文件目录
		 * @var string
		 */
		var $compile_dir = 'templates_c';
		/**
		 * 是否检查模版临时编译文件
		 * @var boolean
		 */
		var $compile_check  = true;
		/**
		 * 是否强制编译
		 * @var boolean
		 */
		var $force_compile = false;
		/**
		 * 是否缓存
		 * @var boolean
		 */
		 var $caching = 0;
		/**
		 * 存放缓存目录
		 * @var string
		 */
		 var $cache_dir = 'cache';
		 /**
		 * 缓存存活期
		 * @var int
		 */
		 var $cache_life_time  =  3600;
		 /**
		 * 是否采用客户端缓存
		 * @var boolean
		 */
		 var $client_caching  =  false;
		/**
		 * 语言包路径
		 * @var string
		 */
		 var $lang_dir = '';
		 /**
		 * 全局语言变量名
		 * @var string
		 */
		 var $global_lang_name = 'global';
		 /**
		 * 是否加载语言包
		 * @var boolean
		 */
		 var $compile_lang = false;
		 /**
		 * 模板标签左定界符
		 * @var string
		 */
		 var $left_delimiter  =  '<';
		 /**
		 * 模板标签右定界符
		 * @var string
		 */
	     var $right_delimiter =  '>';
	     /**
		 * 模版临时编译文件前缀
		 * @var string
		 */
		 var $compilefile_prefix = '&&^';
	     /**
		 * 模板变量左定界符
		 * @var string
		 */
		 var $tag_left_delim = '[';
	     /**
		 * 模板变量右定界符
		 * @var string
		 */
		 var $tag_right_delim = ']';
	     /**
		 * 模板预先解析函数
		 * @var array
		 */
		 var $parse_first_function = array();
	     /**
		 * 模板预后解析函数
		 * @var array
		 */
		 var $parse_filter_function = array();
	     /**
		 * 模板变量存放数组
		 * @var boolean
		 */
		 var $tpl_vars = array();
	     /**
		 * 是否修复模板
		 * @var boolean
		 */
		 var $check_tpl_modify = true;
	     /**
		 * 是否强制编译
		 * @var boolean
		 */
		 //var $force_compile = false;
	     /**
		 * 是否自动修复
		 * @var boolean
		 */
		 var $auto_repair = false;
	     /**
		 * 模板内容源取出函数
		 * @var boolean
		 */
		 var $source = NULL;
	     /**
		 * 编译器文件名
		 * @var string
		 */
	     var $compiler_file = 'compiler.class.php';
	     /**
		 * 编译器类名
		 * @var string
		 */
	     var $compiler_class = 'Compiler';
		 
		
		
		/**
		 * constructor
         * 
         * will always fail, because this is an abstract class!
		 */
		function Template($params)
		{
			if (isset($params['compiler_file']))  $this->compiler_file  = $params['compiler_file'];
			
			if (isset($params['compiler_class'])) $this->compiler_class = $params['compiler_class'];
			
			if (isset($params['template_dir']))   $this->template_dir 	= $params['template_dir'];
	
			if (isset($params['compile_dir'])) 	  $this->compile_dir 	= $params['compile_dir'];
	
			if (isset($params['cache_dir'])) 	  $this->cache_dir  	= $params['cache_dir'];
	
			if (isset($params['lang_dir'])) 	  $this->lang_dir 		= $params['lang_dir'];
		}

		/**
		 * assign a {@link Logger} object to the database
		 * 
         * @see Logger
         * @param object $logger reference to a {@link Logger} object
		 */
		function setLogger(&$logger)
		{
			$this->logger =& $logger;
		}
		/**
		 * 分配模板变量
         * @var $tpl_var,$value
		 */
		function assign($tpl_var, $value = null)                    
	    {
	
	        if (is_array($tpl_var))
	        {                               
	            foreach ($tpl_var as $key => $val) 
	            {
	                if ($key != '') 
	                {
	                    $this->tpl_vars[$key] = $val;
	                }
	            }
	        } 
	        else 
	        {
	            if ($tpl_var != '')
	            {
	                $this->tpl_vars[$tpl_var] = $value;
	            }
	        }
	    }
	    
	    /**
		 * 以引用方式分配模板变量
         * @var $tpl_var,&$value
		 */
	    function assignByRef($tpl_var, &$value)
	    {
	
	        if ($tpl_var != '')
	
	            $this->tpl_vars[$tpl_var] = &$value;
	
	    }
	    /**
		 * 清空所有模板变量
		 */	
		function clearAllAssign()                
	    {
	        $this->tpl_vars = array();
	    }
	    
	    /**
		 * 注册模板预先解析函数
         * @var $function_name
		 */
	    function registerFirstFunction($function_name) 
		{
			$this->parse_first_function[] = $function_name;
		}
		/**
		 * 注册模板预先解析函数
         * @var $function_name
		 */
	    function registerFilterFunction($function_name) 
		{
			$this->parse_filter_function[] = $function_name;
		}	
		/**
		 * 是否编译过
         * @var $function_name
		 */
		function isCompiled() 
		{
			if(!file_exists($this->compile_name)) return false;
	
			$expire =  (filemtime($this->compile_name) == filemtime($this->template_name)) ? true : false;
	
			if($expire) return true;
	
			else return false;
		}
		
		/**
		 * 生成哈希数
		 *
		 * @param string $str
		 * @return int
		 */
		function hash($str)
		{
			$hash = 0;
			$n = strlen($str);
			 
			for ($i = 0; $i <$n; $i++)
			{
				$hash ^= (ord($str[$i]) <<($i & 0x0f));
			}
			
			return $hash % 701819;
		}
		
		/**
		 * 得到缓存子目录
		 *
		 * @param string  $file_name 文件名
		 * @return string
		 */
		function getCacheDir($file_name,$is_mkdir=true)
		{
			$hash = $this->hash($file_name);
			$hash = sprintf('%06u',$hash);
			
			$dir_1 = substr($hash, -4, 2);
			$dir_2 = substr($hash, -2);
			
			$cache_dir = $this->cache_dir . 'cache/' . $dir_1 . '/' . $dir_2 . '/';
			
			if($is_mkdir){
				
				if(!is_dir($cache_dir))
				{
					mkdir($cache_dir , 0755 , 1);
				}
				
			}
			
			return $cache_dir;
		}
		
		/**
		 * 得到缓存目录
		 *
		 * @param string  $file_name 文件名
		 * @return string
		 */
		function getFirstCacheDir($file_name,$is_mkdir=true)
		{
			$hash = $this->hash($file_name);
			$hash = sprintf('%06u',$hash);
			
			$dir_1 = substr($hash, -4, 2);
			
			$cache_dir = $this->cache_dir . 'cache/' . $dir_1;
			
			if($is_mkdir){
				
				if(!is_dir($cache_dir))
				{
					mkdir($cache_dir , 0755 , 1);
				}
				
			}
			
			return $cache_dir;
		}
		
		/**
		 * 是否生成过缓存
         * @var $file_name,$cache_id
		 */
		function isCached($file_name, $cache_id = NULL)
		{
			if($this->cached) return true;
	
			$this->cache_name = $this->getCacheDir($this->template_dir . $file_name . $cache_id) . md5($this->template_dir . $file_name . $cache_id) . '.cache';
	
	 		if(!file_exists($this->cache_name)) return false;
	
	 		if(!($mtime = filemtime($this->cache_name))) return false;
	
			$this->cache_expire_time = $mtime + $this->cache_life_time - time();
	
	 		if(($mtime + $this->cache_life_time) < time()) 
	 		{
				unlink($this->cache_name);
				return false;
			} 
			else 
			{
				$this->cached = true;
				return true;
			}
		}	
		/**
		 * 格式化编译文件名
		 * @var $file_name
		 */	
		function format($file_name)
		{
			$file_name = str_replace('/', '&' ,$file_name);
			$file_name = str_replace('\\', '&' ,$file_name);
			$file_name = str_replace('..', '^' ,$file_name);
	
			return $file_name;
		}
		/**
		 * 取出待编译内容
		 * @var $file_name,$compile
		 */	
		function fetch($file_name, $compile = 0)
		{
			$this->template_name = $this->template_dir . $file_name;
			$this->compile_name  = $this->compile_dir . $this->compilefile_prefix . $this->format($this->template_dir . $file_name);
			
			$this->lang_name = $this->lang_dir . $file_name . '.lang.php';
	        $this->global_lang_name =  $this->lang_dir . $this->global_lang_name .'.lang.php';
	       
			ob_start();

			if(file_exists($this->lang_name))
			{
				include($this->lang_name);
			}
			
			if(file_exists($this->global_lang_name))
			{
				include($this->global_lang_name);
			}
			
			if(!$this->isCompiled()) 
			{
				if($this->compile($this->template_name))
				{
					include($this->compile_name);
				}
			}
			else 
			{
				include($this->compile_name);
			}

			$contents = ob_get_contents();

			ob_end_clean();

			if($compile) 
			{
				$contents =  $this->compileOutput($contents);
			}
			
			if(!empty($this->parse_filter_function))
			{
				foreach($this->parse_filter_function as $var)
				{
					if(function_exists($var))
					{
						$contents = $var($contents);
					}
				}
			}
			

 			return $contents;
		}
		
		/**
		 * 取出缓存文件
		 * @var $file_name,$cache_id,$compile
		 */	
		function fetchCache($file_name, $cache_id, $compile=0)          
		{
			$this->cache_name = $this->getCacheDir($this->template_dir . $file_name . $cache_id) . md5($this->template_dir . $file_name . $cache_id) . '.cache';
	
			if($fp = @fopen($this->cache_name, 'r')) 
			{
				$contents = fread($fp, filesize($this->cache_name));
				fclose($fp);
				return $contents;
			} 
			else 
			{
				$contents = $this->fetch($file_name, $compile);
	
				if($fp = @fopen($this->cache_name, 'w')) 
				{
					fwrite($fp, $contents);
					fclose($fp);
				} 
				else 
				{
					die('Unable to write cache.');
				}
				
				return $contents;
			}
		}
		
		/**
		 * 删除缓存文件
		 * @var $file_name,$cache_id
		 */	
		function clear_cache($file_name, $cache_id)
		{
			$this->cache_name = $this->getCacheDir($this->template_dir . $file_name . $cache_id) . md5($this->template_dir.$file_name . $cache_id) . '.cache';

			return @unlink($this->cache_name);
		}
		
		/**
		 * 清空缓存目录
		 * @return unknown_type
		 */
		function clear_cache_dir($file_name, $cache_id){
			
			$this->cache_name = $this->getFirstCacheDir($this->template_dir . $file_name . $cache_id,false).'/';
			
			rmdir_rf($this->cache_name);
		
			return $b;
		}
		
		/**
		 * 显示
		 * @var $file_name,$enable_gzip:压缩技术
		 */	
	    function display($file_name, $enable_gzip = NULL)     
		{
			if($_GET['debug'] == 'true')
			{
				$this->displayDebug();
			}
			elseif(ENABLE_GZIP || $enable_gzip) 
			{
				$buffer = $this->fetch($file_name);
				
	        	ob_start('ob_gzhandler');
	        	
	        	print $buffer;
	        	
			} 
			else 
			{
				print($this->fetch($file_name));
			}
			
		}
		/**
		 * 显示缓存
		 * @var $file_name,$cache_id,$enable_gzip:压缩技术
		 */	
		function displayCache($file_name, $cache_id = NULL, $enable_gzip = NULL) 
		{
			global $sys_conf_var;
			if($this->client_caching && $sys_conf_var['system']['cache_life_time'])
			{
				header('Cache-Control: public');
				header('Pragma:');

				$lastmod = time() + $this->cache_expire_time;
				header("Last-Modified: " . gmdate("D, d M Y H:i:s",  $lastmod) . " GMT");
				header("Expires: " .gmdate ("D, d M Y H:i:s", time() + $this->cache_expire_time). " GMT");

				$cache_file = $sys_conf_var['system']['root'] . $this->getCacheDir($this->template_dir . $file_name . $cache_id) . md5($this->template_dir . $file_name . $cache_id) . '.cache';

				if ( isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])==$lastmod && filemtime($cache_file)<=$lastmod )
				{
					@header('Status: 304 Not Modified', true, 304);
					exit;
				}
			}

			if(ENABLE_GZIP || $enable_gzip)
			{
				$buffer = $this->fetchCache($file_name, $cache_id);
	        	ob_start('ob_gzhandler');
	        	print $buffer;
			} 
			else
			{
				print($this->fetchCache($file_name, $cache_id));
			}
		}
		
		/**
		 * 运行缓存
		 * @var $file_name,$cache_id,$enable_gzip:压缩技术
		 */	
		function runCache($file_name, $cache_id = NULL, $enable_gzip = NULL) 
		{
			if(ENABLE_GZIP || $enable_gzip) 
			{
				$buffer = $this->fetchCache($file_name, $cache_id);
	        	ob_start('ob_gzhandler');
	        	print $buffer;
			} 
			else 
			{
				print($this->fetchCache($file_name, $cache_id));
			}
		}
		
		
	/**输出调试信息
		 * 
		 * @return void
		 */
		function displayDebug()
		{
			global $sys_conf_var;
			
			if($sys_conf_var['version']['sys_debug'] === false)
			{
				header("HTTP/1.1 404 Not Found");
				header("Status: 404 Not Found");
				return false;
			}
			/*ob_start();
				
			print_r($this->tpl_vars);
			
			$content = ob_get_contents();
			
			ob_clean();
			
			$content = str_replace("\n",'<br>',$content);
			$content = str_replace(" ",'&nbsp;',$content);
			$content = preg_replace("/(\[[a-zA-Z0-9\_\-]+\])/i","<font color='red'>\${1}</font>",$content);*/
			$xml_string = '<?xml version="1.0" encoding="utf-8"?>';
			$xml_string .= '<TemplateVar>';
			$xml_string .= $this->makeXml($this->tpl_vars);
			$xml_string .= '</TemplateVar>';
			//$xml_string .= '<GoodsCategoryList><item0><ID value="22" /></item0></GoodsCategoryList>';
			
			header("Content-type: text/xml");
			
			print $xml_string;
		}
		
		function makeXml($array)
		{
			$xml_string = '';
			
			foreach($array as $key => $var)
			{
			     if(is_array($var))
			     {
			     	$xml_string .= '<' . $this->formatXmlKey($key) . '>';
			     	$xml_string .= $this->makeXml($var);
			     	$xml_string .= '</' . $this->formatXmlKey($key) . '>';
			     }
			     else
			     {
			     	$xml_string .= '<' . $this->formatXmlKey($key) . ' value="' . $this->formatXmlVar($var) . '" />';
			     }
			     	
			}
			
			return $xml_string;
		}
		
		function formatXmlVar($str)
		{
			$str = str_replace('"',"&quot;",$str);
			$str = str_replace('&',"&amp;",$str);
			$str = str_replace('<',"&lt;",$str);
			$str = str_replace('>',"&gt;",$str);
			
			return $str;
		}
		
		function formatXmlKey($str)
		{
			if(is_int($str))
			{
				$str = 'item' . $str;
			}
			
			return $str;
		}
	
	}

}
?>